package com.mall.gateway.permission;

import com.alibaba.fastjson.JSON;
import com.mall.common.util.JwtToken;
import com.mall.common.util.MD5;
import com.mall.permission.model.Permission;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @author Darren
 * @date 2022-10-20 10:22
 * Description：
 */
@Component
public class AuthorizationInterceptor {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 令牌IP校验
     *
     * @param token
     * @param clientIp
     * @return
     */
    public static Map<String, Object> jwtVerify(String token, String clientIp) {
        if (token == null)
            return null;
        try {
            // 解析token,获取数据
            Map<String, Object> map = JwtToken.parseToken(token);
            // 令牌中的ip
            String jwtIp = map.get("ip").toString();
            // ip校验
            if (jwtIp.equals(MD5.md5(clientIp))) {
                return map;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 校验是否需要拦截指定请求
     *
     * @param exchange
     * @return
     */
    public boolean isIntercept(ServerWebExchange exchange) {
        ServerHttpRequest request = exchange.getRequest();
        // uri
        String uri = request.getURI().getPath();
        // 请求方式
        String method = request.getMethodValue();
        // 路由URI信息
        URI routerUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);

        // 从redis获取完全匹配信息
        List<Permission> permissionMatch0 = (List<Permission>) redisTemplate.boundHashOps("RolePermissionAll").get("PermissionListMatch0");
        // 完全匹配
        Permission permission = this.match0(permissionMatch0, uri, method, routerUri.getHost());

        // 如果完全匹配没有,则进行通配符匹配
        if (permission == null) {
            // 从redis获取通配符匹配信息
            List<Permission> permissionMatch1 = (List<Permission>) redisTemplate.boundHashOps("RolePermissionAll").get("PermissionListMatch1");
            // 通配符匹配
            permission = this.match1(permissionMatch1, uri, method, routerUri.getHost());
        }

        // 如果此时permission为空,则表示不需要进行权限检验
        return permission != null;
    }

    /**
     * 角色权限校验
     *
     * @param exchange
     * @param tokenMap
     * @return
     */
    public boolean rolePermissionVerify(ServerWebExchange exchange, Map<String, Object> tokenMap) {
        if (exchange == null || tokenMap == null)
            return false;

        ServerHttpRequest request = exchange.getRequest();
        // uri
        String uri = request.getURI().getPath();
        // 请求方式
        String method = request.getMethodValue();
        // 路由URI信息
        URI routerUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);

        // 获取token中的角色
        String[] roles = tokenMap.get("roles").toString().split(",");
        // 当前角色权限
        Permission permission = null;

        // 循环角色,获取角色权限
        for (String role : roles) {
            /**
             * 完全匹配方式
             */
            String key0 = "Role_0_" + role;
            // 从redis获取角色权限数据
            Object rolePermissionList0 = redisTemplate.boundHashOps("RolePermissionMap").get(key0);
            if (rolePermissionList0 != null) {
                List<Permission> permissions = JSON.parseArray(JSON.toJSONString(rolePermissionList0), Permission.class);
                // 匹配权限
                permission = this.match0(permissions, uri, method, routerUri.getHost());
            }

            /**
             * 如果完全匹配方式没找到,则进行通配符匹配方式
             */
            if (permission == null) {
                String key1 = "Role_1_" + role;
                // 从redis获取角色权限数据
                Object rolePermissionList1 = redisTemplate.boundHashOps("RolePermissionMap").get(key1);
                if (rolePermissionList1 != null) {
                    List<Permission> permissions = JSON.parseArray(JSON.toJSONString(rolePermissionList1), Permission.class);
                    // 匹配权限
                    permission = this.match1(permissions, uri, method, routerUri.getHost());
                }
            }

            // 找到权限直接退出循环
            if (permission != null)
                break;
        }

        return permission != null;
    }

    /**
     * 完全匹配
     *
     * @param permissionMatch0
     * @param uri
     * @param method
     * @param serviceName
     * @return
     */
    public Permission match0(List<Permission> permissionMatch0, String uri, String method, String serviceName) {
        if (permissionMatch0 == null)
            return null;
        for (Permission permission : permissionMatch0) {
            String matchUri = permission.getUrl();
            String matchMethod = permission.getMethod();
            String matchServiceName = permission.getServiceName();
            // uri匹配
            if (uri.equals(matchUri)) {
                // 请求方式和服务名称匹配
                if ((method.equals(matchMethod) || "*".equals(matchMethod)) && serviceName.equals(matchServiceName))
                    return permission;
            }
        }
        return null;
    }

    /**
     * 通配符匹配
     *
     * @param permissionMatch1
     * @param uri
     * @param method
     * @param serviceName
     * @return
     */
    public Permission match1(List<Permission> permissionMatch1, String uri, String method, String serviceName) {
        if (permissionMatch1 == null)
            return null;
        for (Permission permission : permissionMatch1) {
            String matchUri = permission.getUrl();
            String matchMethod = permission.getMethod();
            String matchServiceName = permission.getServiceName();

            // uri匹配
            // 避免出现多/,先替换一遍
            uri = uri.replaceAll("/+", "/");
            matchUri = matchUri.replaceAll("/+", "/");
            // 如果以/开头,去掉/
            if (uri.startsWith("/"))
                uri = uri.replaceFirst("/", "");
            if (matchUri.startsWith("/"))
                matchUri = matchUri.replaceFirst("/", "");
            String[] uriSplit = uri.split("/");
            String[] matchUriSplit = matchUri.split("/");

            // 开始匹配uri
            boolean uriFlag = false;
            if (uriSplit.length == matchUriSplit.length) {
                for (int i = 0; i < uriSplit.length; i++) {
                    String match = matchUriSplit[i];
                    if (match.contains("{") && match.contains("}")) {
                        match = match.replace("{", "").replace("}", "");
                        // string直接通过
                        if ("string".equals(match))
                            uriFlag = true;
                        // integer需要判断,直接转integer,如果报错就不是integer
                        if ("integer".equals(match)) {
                            try {
                                Integer.valueOf(uriSplit[i]);
                            } catch (NumberFormatException e) {
                                uriFlag = false;
                                break;
                            }
                            uriFlag = true;
                        }
                    } else {
                        if (!uriSplit[i].equals(matchUriSplit[i]))
                            break;
                        else
                            uriFlag = true;
                    }
                }
            }

            // 根据uri匹配结果做后续操作
            if (uriFlag) {
                // 请求方式和服务名称匹配
                if ((method.equals(matchMethod) || "*".equals(matchMethod)) && serviceName.equals(matchServiceName))
                    return permission;
            }
        }
        return null;
    }

    /**
     * 无效路径过滤
     *
     * @param uri
     * @return
     */
    public boolean isInvalid(String uri) {
        RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter("UriBloomFilterArray");
        return bloomFilter.contains(uri);
    }
}
